/*
  The syscall.c file implements System Calls
*/

#include <types.h>
#include <klib.h>
#include <i386_arch.h>
#include <syscall.h>
#include <panic.h>
#include <scheduler.h>
#include <pit.h>


size_t syscall_read(int fd,void *buf,size_t count);
size_t syscall_write(int fd,const void *buf,size_t count);
int syscall_brk(void *data_section_end);
int syscall_submit(const char *name,int phase,int period,int exec_time,int deadline);
int syscall_yield(void);
dword_t syscall_time(void);
int syscall_quit(int code);


void demultiplex_syscall(uregs_t *uregs)
{
  dword_t p1,p2,p3,p4,p5;
  enum syscalls_t syscall_number;
  dword_t retval;

  syscall_number=(enum syscalls_t)(uregs->eax);

  /*
  printf("I got a syscall: %d\n",syscall_number);
  context_dump(uregs);
  */
  
  /*
  asm("int $0x10");
  asm("int $0x10");
  asm("int $0x10");
  //context_dump(uregs);
  asm("hlt");
  */


  p1=(dword_t)(uregs->ebx);
  p2=(dword_t)(uregs->ecx);
  p3=(dword_t)(uregs->edx);
  p4=(dword_t)(uregs->esi);
  p5=(dword_t)(uregs->edi);

  switch (syscall_number)
    {
    case SYS_NONE:
      retval=0;
      break;
      
    case SYS_READ:
      retval=(dword_t)syscall_read((int)p1,(void *)p2,(size_t)p3);
      break;

    case SYS_WRITE:
      retval=(dword_t)syscall_write((int)p1,(const void *)p2,(size_t)p3);
      break;

    case SYS_BRK:
      retval=(dword_t)syscall_brk((void *)p1);
      break;

    case SYS_SUBMIT:
      retval=(dword_t)syscall_submit((const char *)p1,(int)p2,(int)p3,(int)p4,(int)p5);
      break;

    case SYS_YIELD:
      retval=(dword_t)syscall_yield();
      break;

    case SYS_TIME:
      retval=(dword_t)syscall_time();
      break;

    case SYS_QUIT:
      retval=(dword_t)syscall_quit((int)p1);
      break;      

    default:
      printf("Error: unknown syscall %d.\n",p1);
      context_dump(uregs);
      panic("Unknown syscall.");
    }

  // Adjust return value
  uregs->eax=retval;
  uregs->ebx=syscall_number;
}

size_t syscall_read(int fd,void *buf,size_t count)
{  
  return 0;
}

size_t syscall_write(int fd,const void *buf,size_t count)
{  
  char *p;

  // Note: file descriptor fd is not used for now 
  // because the file system hasn't been implemented yet.

  // allocate a temporary buffer
  p=(char *)malloc(count+1);

  // make a local copy in kernel memory
  memcpy(p,buf,count);

  // make sure buffer is zero-terminated
  p[count]='\0';

  // print message
  printf("%s",p);

  // free temporary buffer
  free(p);  

  return count;
}

int syscall_brk(void *data_section_end)
{  
  return 0;
}

int syscall_submit(const char *name,int phase,int period,int exec_time,int deadline)
{  
  image_t *img;
  process_t *p;
  char *tmp_name;


  img=find_image(name);

  // unable to find image
  if (img==NULL)
    {
      printf("No such executable image: %s\n",name);
      panic("Can't find executable image.");
      return 1;
    }

  p=create_process(img);

  // set real time parameters
  p->phase=phase;
  p->period=period; 
  p->deadline=deadline;
  p->exec_time=exec_time;

  //p->exec_remaining=exec_time;

  // process type: periodic
  p->type=TYPE_PERIODIC;  

  // set process state to ready
  // this will change when using EDF scheduler
  // because we will need to make an admission test
  //p->state=PS_READY;
  p->state=PS_TERMINATED;

  if (acceptancy_test(p))
    schedule();
  else
    return 1;  // can't run process

  return 0;
}

int syscall_yield(void)
{ 
  // call scheduler,
  // which will cause another process to run
  // if there are any ready ones
  schedule();

  return 0;
}

dword_t syscall_time(void)
{  
  dword_t days,ms,us;

  // retrieve days milli-seconds, and micro-seconds
  pit_get_time(&days,&ms,&us);

  // return time in milli-seconds
  // 32 bit will cause it to wrap around approximately every:
  // 49 days, 17 hours, 2 minutes, 47 seconds and 295 milli-seconds.
  // should fix this someday... using 64 bit for time keeping

  // return time in ms
  // 86400000 is the number of ms in a day
  return days*86400000L + ms;
}

int syscall_quit(int code)
{
  // mark this process for termination
  // scheduler will do that
  current_process->state=PS_TERMINATED;

  schedule();

  return 0;
}
